﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MessageServer.channels;
using MessageServer.utilities;

namespace MessageServer.channels
{
    /**
     * Convienience class to be used for firing events, for channel state changes
     * bind channels etc. 
     * */
    public class ChannelUtils
    {
        /**
         * Construct and return a new EventTracker class that can be used to track
         * the event passed into the function.
         * */
        public static EventTracker getEventTracker(ChannelEvent evt)
        {
            return new DefaultEventTracker(evt);
        }

        //helper function used by other functions in this class to fire off a 
        //channel state changed event. This function streams the state change of the 
        //channel up the pipe passed to the function.
        private static ChannelEvent fireChannelStateEvent(Channel channel, ChannelPipeline pipeline, 
            ChannelStateEvent.ChannelState state)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:fireChannelStateEvent:Firing Channel state change event:" + state);
            ChannelEvent evt = new ChannelStateEvent(channel, state);
            pipeline.streamup(evt);
            return evt;
        }
        
        /**
         * Call this function to fire an channel open event. This will stream up the event
         * on the Channel's pipe passed to this function.
         * */
        public static EventTracker fireOpenEvent(Channel channel)
        {
            ChannelEvent track = fireChannelStateEvent(channel, channel.pipeline, ChannelStateEvent.ChannelState.OPEN);
            return track.tracker;
        }

        /**
         * Call this function to fire an channel bound event. This will stream up the event
         * on the Channel's pipe passed to this function.
         * */
        public static EventTracker fireBoundEvent(Channel channel)
        {
            ChannelEvent evt = fireChannelStateEvent(channel, channel.pipeline, ChannelStateEvent.ChannelState.BOUND);
            return evt.tracker;
        }

        /**
         * Call this function to fire an channel close event. This will stream up the event
         * on the Channel's pipe passed to this function.
         * */
        public static EventTracker fireCloseEvent(Channel channel, EventStateListener listener)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:fireChannelCloseEvent:Firing Channel state change event:");
            ChannelEvent evt = new ChannelStateEvent(channel, ChannelStateEvent.ChannelState.CLOSED);
            if (listener != null)
                evt.tracker.addListener(listener);
            channel.pipeline.streamup(evt);
            return evt.tracker;
        }

        /**
         * Call this function to fire a upstream msg event for the object passed. This will stream up the event
         * on the Channel's pipe passed to this function.
         * */
        public static EventTracker fireUpstreamMsgEvent(Channel channel, Object obj)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:fireUpstreamMsgEvent:Firing upstream message event.");
            ChannelEvent evt = new UpstreamMessageEvent(channel, obj);
            channel.pipeline.streamup(evt);
            return evt.tracker;
        }

        public static EventTracker fireExceptionEvent(ChannelEvent evt, Exception e)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:fireExceptionEvent: Firing an exception event for " + e.Message);
            ChannelEvent eevt = new ExceptionEvent(evt.channel, e, evt);
            evt.channel.pipeline.streamup(eevt);
            return eevt.tracker;
        }

        public static EventTracker fireExceptionEvent(Channel channel, Exception e)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:fireExceptionEvent: Firing an exception event for " + e.Message);
            ChannelEvent evt = new ExceptionEvent(channel, e, null); //this when it occurs without event
            channel.pipeline.streamup(evt);
            return evt.tracker;
        }

        /**
         * Call this function to bind the channel to a given local port. 
         * This function will stream down the channel pipe the bound event and 
         * at the end sink it into the ChannelSink of the pipe. 
         * */
        public static EventTracker bind(Channel channel, int port)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:bind:Binding channel to port " + port);
            DownstreamStateEvent evt = new DownstreamStateEvent(channel, ChannelStateEvent.ChannelState.BOUND, port);
            channel.pipeline.streamdown(evt);
            return evt.tracker;
        }

        /**
         * Call this function to write the data represented by the buffer object to the 
         * channel and return the Eventtracker.
         * */
        public static EventTracker write(ChannelEvent srcevt, Channel channel, ByteBuffer replyto, ByteBuffer buffer)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils::write: writing buffer");
            DownStreamMsgEvent evt = new DownStreamMsgEvent(srcevt, replyto, buffer);
            channel.pipeline.streamdown(evt);
            return evt.tracker;
        }

        /**
         * Call this function to write the data represented by the buffer object to the 
         * channel and return the Eventtracker.
         * */
        public static EventTracker write(ChannelEvent srcevt, Channel channel, Object replyto, Object buffer)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils::write: writing buffer");
            DownStreamMsgEvent evt = new DownStreamMsgEvent(srcevt, replyto, buffer);
            channel.pipeline.streamdown(evt);
            return evt.tracker;
        }

        /**
         * This function is called when a write has been completed to notify listeners about
         * the write.
         * */
        public static EventTracker fireWriteCompleted(Channel channel, ByteBuffer buffer)
        {
            ChannelLogger.LOG.LogDebug("ChannelUtils:fireWriteCompleted:Firing Write Completed event:");
            ChannelEvent evt = new ChannelStateEvent(channel, ChannelStateEvent.ChannelState.SENT, buffer);
            channel.pipeline.streamup(evt);
            return evt.tracker;
        }

    }
}